<template>
  <el-form class="form" ref="form"
    :model="formModel"
    :rules="formOptionsVariable.rules"
    v-bind="formOptionsVariable.attributes"
    v-on="formOptionsVariable.events"
  >
    <div class="form__body" :class="`grid-cols-${formOptionsVariable.columnNumber}`">
      <template v-for="(formItem,index) in formOptionsVariable.formItems" :key="'form-item' + index">
        <el-form-item
          v-if="formItem.show === 'function'?formItem.show(formModel):formItem.show"
          class="form__item"
          :class="`col-span-${formItem.grid}`"
          :prop="formItem.prop"
          :label="formItem.label"
          v-bind="formItem.formAttr"
        >
          <!-- 表单项名称 -->
          <template #label>
            <div class="form__item__label">
              <slot :name="'form-item-label-' + formItem.prop" :formItem="formItem">
                <span>{{ formItem.label }}</span>

                <el-tooltip v-if="formItem.tip" :content="formItem.tip">
                  <el-icon class="cursor-pointer">
                    <QuestionFilled/>
                  </el-icon>
                </el-tooltip>
              </slot>
            </div>
          </template>

          <!-- 表单项组件 -->
          <div class="form__item__component">
            <slot :name="'form-item-component-' + formItem.prop" :formItem="formItem">
              <el-input class="w-full"
                v-if="formItem.type === ArkFormConstant.operation.input"
                v-model.trim="formModel[formItem.prop]"
                v-bind="formItem.attributes"
                v-on="formItem.events"
              />

              <el-select class="w-full"
                v-else-if="formItem.type === ArkFormConstant.operation.select"
                v-model="formModel[formItem.prop]"
                v-bind="formItem.attributes"
                v-on="formItem.events"
              >
                <template v-if="formItem.selectOptions.groups">
                  <el-option-group v-for="(groupItem,groupIndex) in formItem.selectOptions.groups"
                    :key="'form-item-component-select'+groupIndex"
                    :label="groupItem.label"
                  >
                    <el-option v-for="(optionItem,optionIndex) in groupItem.options"
                      :key="'form-item-component-select'+groupIndex+optionIndex"
                      :label="optionItem.label"
                      :value="optionItem.value"
                    />
                  </el-option-group>
                </template>
                <template v-else-if="formItem.selectOptions.options">
                  <el-option v-for="(optionItem,index) in formItem.selectOptions.options"
                    :key="'form-item-component-select'+index"
                    :label="optionItem.label"
                    :value="optionItem.value"
                  />
                </template>
              </el-select>

              <el-cascader class="w-full"
                v-else-if="formItem.type === ArkFormConstant.operation.cascader"
                v-model="formModel[formItem.prop]"
                v-bind="formItem.attributes"
                v-on="formItem.events"
              />

              <el-date-picker class="w-full"
                v-else-if="formItem.type === ArkFormConstant.operation.datePicker"
                v-model="formModel[formItem.prop]"
                v-bind="formItem.attributes"
                v-on="formItem.events"
              />

              <el-time-picker class="w-full"
                v-else-if="formItem.type === ArkFormConstant.operation.timePicker"
                v-model="formModel[formItem.prop]"
                v-bind="formItem.attributes"
                v-on="formItem.events"
              />

              <el-time-select class="w-full"
                v-else-if="formItem.type === ArkFormConstant.operation.timeSelect"
                v-model="formModel[formItem.prop]"
                v-bind="formItem.attributes"
                v-on="formItem.events"
              />
            </slot>
          </div>
        </el-form-item>
      </template>
    </div>

    <el-form-item class="form__btn"
      v-if="formOptionsVariable.button.show === 'function'?formOptionsVariable.button.show():formOptionsVariable.button.show"
      :class="{
        'form__btn__across':formOptionsVariable.button.layout===ArkFormConstant.layout.across,
        'form__btn__vertical':formOptionsVariable.button.layout===ArkFormConstant.layout.vertical,
      }"
    >
      <slot name="form-item-btn">
        <el-button
          v-if="formOptionsVariable.button.reset.show === 'function'?formOptionsVariable.button.reset.show():formOptionsVariable.button.reset.show"
          v-bind="formOptionsVariable.button.reset.attributes"
          v-on="formOptionsVariable.button.reset.events"
          @click="resetFormModel()"
        >
          {{ formOptionsVariable.button.reset.text }}
        </el-button>

        <el-button
          v-if="formOptionsVariable.button.confirm.show === 'function'?formOptionsVariable.button.confirm.show():formOptionsVariable.button.confirm.show"
          v-bind="formOptionsVariable.button.confirm.attributes"
          v-on="formOptionsVariable.button.confirm.events"
          @click="clickConfirmDialog()"
          :loading="confirmLoading"
        >
          {{ formOptionsVariable.button.confirm.text }}
        </el-button>
      </slot>
    </el-form-item>
  </el-form>
</template>

<script>
import {defineComponent} from 'vue';
import _ from 'lodash';
import ArkFormConstant from './ArkFormConstant.js';

export default defineComponent({
  name: 'ArkForm',
  props: {
    /**
     * 表单配置对象
     *  - attributes：表单v-bind对象
     *  - events：表单v-on对象
     *  - rules：表单项验证规则
     *  - validateFail：表单项验证失败，参数为验证失败数据
     *  - columnNumber：表单内部列数
     *  - formItems[]：表单项v-bind对象与其他配置
     *   - show：是否展示。可为boolean类型，可为方法。为方法时参数formModel，返回boolean类型
     *   - defaultShow：表单项是否展示默认值，默认等于show
     *   - grid：该表单项占网格布局中的几列
     *   - prop：与formModel对象的键对应
     *   - label：标签文字
     *   - tip：标签文字提示内容
     *   - type：表单项组件类型
     *   - defaultValue：表单项组件默认值
     *   - attributes：表单项组件v-bind对象
     *   - selectOptions：表单项组件选择框select的选项。不分组对象内容为{options[{label,value}]}，分组对象内容为{groups[{label,options[{label,value}]}]}
     *   - events：表单项组件v-on对象
     *  - button：按钮
     *   - show：是否展示。可为boolean类型，可为方法
     *   - layout：布局方式，横向或竖向
     *   - confirm：确认按钮
     *    - show：是否展示。可为boolean类型，可为方法
     *    - attributes：按钮v-bind对象
     *    - events：按钮v-on对象
     *    - click：按钮click事件，参数formModel
     *    - text：按钮文字
     *   - reset：重置按钮
     *    - show：是否展示。可为boolean类型，可为方法
     *    - attributes：按钮v-bind对象
     *    - events：按钮v-on对象
     *    - click：按钮click事件，参数formModel
     *    - text：按钮文字
     */
    formOptions: {
      type: Object,
    },
  },
  data() {
    const thisVue = this;
    return {
      ArkFormConstant: ArkFormConstant,
      formOptionsVariable: {
        attributes: {
          'label-position': 'right',
          'label-width': 'auto',
        },
        events: {},
        rules: {},
        validateFail: () => { },
        columnNumber: 1,
        formItems: [],
        button: {
          show: true,
          layout: ArkFormConstant.layout.across,
          confirm: {
            show: true,
            attributes: {
              type: 'primary',
            },
            events: {},
            click: () => {},
            text: '确定'
          },
          reset: {
            show: true,
            attributes: {},
            events: {},
            click: () => {},
            text: '重置'
          },
        }
      },
      confirmLoading: false,
      formModel: {},
    };
  },
  created() {
    this.initAllOptionsVariable();
  },
  methods: {
    initAllOptionsVariable() {
      // 根据传入formOptions初始化formOptionsVariable
      const formOptions = _.cloneDeep(this.formOptions);
      const formOptionsVariable = _.cloneDeep(this.formOptionsVariable);
      this.formOptionsVariable = this.setFormOptionsVariable(formOptions, formOptionsVariable);
    },
    setFormOptionsVariable(options, optionsVariable) {
      optionsVariable = _.defaultsDeep(options, optionsVariable);
      optionsVariable.formItems.forEach((item, index) => {
        if (item.show === undefined) {
          item.show = true;
        }

        const formItemsOptionsVariable = {
          show: true,
          defaultShow: item.show,
          grid: 1,
          prop: index.toString(),
          label: '表单项名称',
          formAttr: {},
          tip: item.tip,
          type: ArkFormConstant.operation.input,
          defaultValue: '',
          attributes: item.type === ArkFormConstant.operation.select ?
            {placeholder: '请选择', clearable: true} : {placeholder: '请输入', clearable: true},
          selectOptions: item.type === ArkFormConstant.operation.select ? {options: []} : null,
          events: {},
        };

        _.defaultsDeep(item, formItemsOptionsVariable);

        this.formModel[item.prop] = item.defaultValue;
      });

      return optionsVariable;
    },
    clickConfirmDialog() {
      let form = this.$refs.form;
      if (!form) {
        this.confirmLoading = true;

        Promise.resolve(this.formOptionsVariable.button.confirm.click(this.formModel)).finally(() => {
          this.confirmLoading = false;
        });
        return;
      }

      form.validate((isValid, invalidFields) => {
        if (!isValid) {
          this.formOptionsVariable.validateFail(invalidFields);
          return;
        }

        this.confirmLoading = true;
        Promise.resolve(this.formOptionsVariable.button.confirm.click(this.formModel)).finally(() => {
          this.confirmLoading = false;
        });
      });
    },
    resetFormModel() {
      this.formOptionsVariable.button.reset.click(this.formModel);

      let form = this.$refs.form;
      if (form) {
        form.resetFields();
      }

      this.formOptionsVariable.formItems.forEach((item) => {
        item.show = item.defaultShow;
      });
    },

    // 供外部调用，内部未使用
    getElFormInstance() {
      return this.$refs.form;
    },
    setFormModel(formModel) {
      this.formModel = _.defaultsDeep(formModel, this.formModel);
    },
    setFormItemShow(prop, show) {
      this.formOptionsVariable.formItems.forEach(item => {
        if (item.prop !== prop) {
          return;
        }

        item.show = show;
      });
    },

    _preInit() {
      let options = _.cloneDeep(this.options);

      let defaultOptions = {
        defaultFooterBtnConfig: {
          show: true,
          layout: ArkFormConstant.layout.leftRight,
        },
      };
    },
  },
});
</script>

<style scoped>
.form {
  .form__body {
    @apply grid;

    .form__item {
      @apply flex items-center gap-x-2;

      :deep(.el-form-item__label) {
        display: flex;
      }

      .form__item__label {
        @apply shrink-0 flex items-center gap-x-0.5;
      }

      .form__item__component {
        @apply flex-1;

        & > div {
          @apply w-full;
        }
      }
    }
  }

  .form__btn {
    &__across {
      :deep(.el-form-item__content) {
        @apply flex gap-x-3 justify-end;

        & > button {
          @apply m-0;
        }
      }
    }

    &__vertical {
      :deep(.el-form-item__content) {
        @apply flex flex-col gap-y-3 justify-center;

        & > button {
          @apply w-full m-0;
        }
      }
    }
  }
}
</style>
